package org.dei.perla.device.multigateway;

import java.util.ArrayList;
import org.dei.perla.sys.configuration.ConfigurationManager;
import org.dei.perla.sys.device.adapter.AdapterServerConcrete;
import org.dei.perla.sys.device.adapter.Gateway;
import org.dei.perla.sys.device.channel.ChannelManager;
import org.dei.perla.sys.device.channel.concrete.socket.SocketChannelManager;
import org.dei.perla.sys.device.channel.frame.address.concrete.SocketFrameAddress;
import org.dei.perla.sys.device.fpcfactory.FpcFactory;
import org.dei.perla.utils.logger.ConsoleLogger;
import org.dei.perla.utils.logger.Logger;
import org.dei.perla.utils.logger.LogRecord.Verbosity;
import org.dei.perla.utils.messages.AdapterChannelMessage;
import org.dei.perla.utils.messages.MsgUtility;

public class TestMoreGateway {

	/**
	 * Test di una rete PERLA
	 * 
	 * La rete simulata è composta da 2 nodi sensori (1a,1b),
	 * 2 nodi gateway e un nodo con macchina FPC.
	 * 
	 * I vari nodi sono interconnessi in questo modo
	 * 
	 * 1a |
	 *    |------| Gateway | Gateway | FPC-Factory
	 * 2a |      
	
	 * Il test dimostra il binding dei sensori e il corretto funzionamento del binding
	 * anche attraverso 2 nodi Gateway.
	 *
	 */
	public static void main(String[] args) {
		
		ConsoleLogger cl = new ConsoleLogger();
		cl.setVerbosity(Verbosity.Low);
		cl.start();
		
		Thread nodeFPC = new NodeFPC(cl, "FPC", "config-socket-node-FPC.xml" );
		Thread nodeG1 = new NodeGateway(cl, "Gateway", "config-socket-node-gateway1.xml", 3000 );
		Thread nodeG2 = new NodeGateway(cl, "Gateway", "config-socket-node-gateway2.xml",1000 );
		Thread nodeS1a = new NodeSensor(cl, "S 1a","config-socket-node-sensor1.xml", "127.0.0.1:4000", 5000);
		Thread nodeS1b = new NodeSensor(cl, "S 1b","config-socket-node-sensor2.xml", "127.0.0.1:4000", 7000);
		
//		 avvia i thread
		nodeFPC.start();
		nodeG2.start();
		nodeG1.start();
		nodeS1a.start();
		nodeS1b.start();
		
		
		try {
			nodeFPC.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		cl.stop();
		System.out.printf("Test finito!\n");

	}

	static private class NodeFPC extends Thread {
		
		Logger l;
		String config;
		String name;
		
		NodeFPC(Logger l,String name, String config) {
			super("Thread " + name);
			this.l = l;
			this.config = config;
			this.name = name;
		}
		
		public void run() {
		
			// Inizializza il nodo sensore
			ConfigurationManager conf = new ConfigurationManager(config);
			conf.loggerAttach(l);

			// Leggi la configurazione
			conf.start();
			ArrayList<ChannelManager> CM = conf.getChannels();
			CM.get(0).loggerAttach(l);
			CM.get(0).start();
			
			// Crea l'adapter server
			AdapterServerConcrete A = new AdapterServerConcrete("Adapter server", CM);
			A.loggerAttach(l);
			A.start();
			
			// Crea l'FPC factory
			FpcFactory F = new FpcFactory (A);
			F.loggerAttach(l);
			F.start();
			
			try {
				sleep(10000);
			} catch (InterruptedException e) {}
			CM.get(0).stop();
			F.stop();
			A.stop();
			
			System.out.printf(name +" termina!\n");
		}
		
	}
	
	static private class NodeGateway extends Thread {
		
		Logger l;
		String config;
		String name;
		int sleep;
		NodeGateway(Logger l,String name, String config, int sleepTime) {
			super("Thread " + name);
			this.l = l;
			this.config = config;
			this.name = name;
			this.sleep = sleepTime;
		}
		
		public void run() {
		
			// Inizializza il nodo sensore
			ConfigurationManager conf = new ConfigurationManager(config);
			conf.loggerAttach(l);

			try {
				sleep(this.sleep);
			} catch (InterruptedException e) {}
			
			// Leggi la configurazione
			conf.start();
			ArrayList<ChannelManager> CM = conf.getChannels();
			Gateway G = conf.getGateway();
			G.loggerAttach(l);
			
			for (int i = 0; i < CM.size(); i++) {
				CM.get(i).loggerAttach(l);
				CM.get(i).start();
			}
			G.start();
			
			try {
				sleep(10000);
			} catch (InterruptedException e) {}
			
			for (int i = 0; i < CM.size(); i++) {
				CM.get(i).stop();
			}
			G.stop();
			
			
			System.out.printf(name +" termina!\n");
		}
		
	}

	static private class NodeSensor extends Thread {
		
		Logger l;
		String config;
		String name;
		String bindAddress;
		int sleep;
		NodeSensor(Logger l,String name, String config, String bindAddress, int sleep) {
			super("Thread " + name);
			this.l = l;
			this.config = config;
			this.name = name;
			this.bindAddress = bindAddress;
			this.sleep = sleep;
		}
		
		public void run() {
			
			// Inizializza il nodo sensore
			ConfigurationManager conf = new ConfigurationManager(config);
			conf.loggerAttach(l);
		
			try {
				sleep(this.sleep);
			} catch (InterruptedException e) {}
			
			// Leggi la configurazione
			conf.start();
			SocketChannelManager CM = (SocketChannelManager) conf.getChannels().get(0);
			CM.loggerAttach(l);
			CM.start();
        
	        //AdapterChannelMessage m = createBindingMessage("MessaggioDiBinding(Questo dovrebbe essere un descrittore XML! :-P )",1, bindAddress);
			AdapterChannelMessage m = createBindingMessage("AAA" + name,(int)(Math.random()*100000), bindAddress);
	        CM.getOutputPipe().enqueue(m);
	        
	        System.out.printf("Il sensore " + name + " ha inviato un messaggio di binding " + m.toString());
	       
	        m = CM.getInputPipe().dequeue();
	        while(m == null) {
	        	m = CM.getInputPipe().dequeue();
	        }
	        
	        System.out.printf("Il sensore " + name + " ha ricevuto " + m.toString());
			
			System.out.printf(name +" termina!\n");
		
		}
		
		private AdapterChannelMessage createBindingMessage (String desc, int bvci, String address){
	         
	         SocketFrameAddress add = new SocketFrameAddress(address);
	         
	         byte[] flagByte = MsgUtility.intToByteArray(MsgUtility.flagBinding);
	         byte[] bvciByte = MsgUtility.intToByteArray((int) (Math.random()*2000000));
	         byte[] descByte = desc.getBytes();
	         
	         byte[] payload = new byte[MsgUtility.flagLength + bvciByte.length + descByte.length];
	        
	         // Flag
	         for(int i = 0; i < MsgUtility.flagLength; i++) {
	        	 payload[i] = flagByte[i];     
	         }
	         
	         // Bvci
	         for (int i = MsgUtility.flagLength; i < MsgUtility.flagLength + MsgUtility.channelIdLength; i++) {
	        	 payload[i] = bvciByte[i-4];
	         }
	         
	         // messaggio
	         for(int i = MsgUtility.flagLength + MsgUtility.channelIdLength; i < MsgUtility.flagLength + descByte.length + bvciByte.length; i++){
	        	 payload[i] = descByte[i-8];
	        	 
	         }
	         
	         return new AdapterChannelMessage(add, payload);

		 }
		
	}
         
 }

